package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"os/exec"

	flag "../spf13/pflag"
)

type argsList struct {
	s, e, l      int
	f            bool
	d, inputFile string
}

//attention: init inputFile in func testFlags
func initFlags(args *argsList, nArgs *[]string) {
	flag.IntVarP(&args.s, "startPage", "s", -1, "The start page")
	flag.IntVarP(&args.e, "endPage", "e", -1, "The end page")
	flag.IntVarP(&args.l, "lineOfPage", "l", 72, "The length of a page")
	flag.StringVarP(&args.d, "destination", "d", "", "The destnatioon of printing")
	flag.BoolVarP(&args.f, "endOfPage", "f", false, "Defind the end symbol of a page")
	flag.Parse()

	*nArgs = flag.Args()
}

func testFlags(args *argsList, nArgs []string) {
	if (args.s == -1) && (args.e == -1) && (args.l == 72) && (args.d == "") &&
		(args.f == false) && (args.inputFile == "") {
		fmt.Fprintf(os.Stderr, "selpg -s startPage -e endPage [-l lineOfPage | -f] [-d destination] [<input_file] [>output_file] [2>error_file]\n")
		os.Exit(1)
	}

	//判断nArgs
	if len(nArgs) == 1 {
		args.inputFile = nArgs[0]
	} else if len(nArgs) == 0 {
		args.inputFile = ""
	} else {
		fmt.Fprintf(os.Stderr, "Too many non-flag arguments!\n")
		os.Exit(1)
	}

	//判断args.s 和 args.e
	if (args.s == -1) || (args.e == -1) {
		fmt.Fprintf(os.Stderr, "The flag -s and -e can't be empty!\n")
		os.Exit(1)
	} else if (args.s < 0) || (args.e < 0) {
		fmt.Fprintf(os.Stderr, "The value of flag -s and -e must be positive!\n")
		os.Exit(1)
	} else if args.s > args.e {
		fmt.Fprintf(os.Stderr, "The value of flag -s can't be bigger than the value of flag -e!\n")
		os.Exit(1)
	}

	//判断args.f 和 args.l
	if (args.f == true) && (args.l != 72) {
		fmt.Fprintf(os.Stderr, "The flag -f and -l can't be used at the same time!\n")
		os.Exit(1)
	} else if args.l <= 0 {
		fmt.Fprintf(os.Stderr, "The value of flag -l must be positive!\n")
		os.Exit(1)
	}
}

//如果命令没有问题，可以调用此函数输出具体参数值
func showArgs(args *argsList) {
	fmt.Println("s: ", args.s)
	fmt.Println("e: ", args.e)
	fmt.Println("l: ", args.l)
	fmt.Println("f: ", args.f)
	fmt.Println("d: ", args.d)
	fmt.Println("inputFile: ", args.inputFile)
}

func proecssArgs(args *argsList) {
	var file *os.File
	var err error
	//读取输入文件，没有输入文件用控制台输入
	if args.inputFile != "" {
		file, err = os.Open(args.inputFile)
		defer file.Close()
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to open file!\n")
			os.Exit(2)
		}
	} else {
		file = os.Stdin
	}
	//判断是打印机输出还是屏幕上输出
	if len(args.d) == 0 {
		output(os.Stdout, file, args)
	} else {
		command := exec.Command("lp", "-d"+args.d)
		var outFile io.WriteCloser
		outFile, err = command.StdinPipe()
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to open pipe!\n")
			os.Exit(2)
		}
		output(outFile, file, args)
	}
}

func output(out interface{}, file *os.File, args *argsList) {
	//注意pageNum的初始值，-f 和 -l 两种方式不一样
	var pageNum int
	if args.f {
		pageNum = 0
	} else {
		pageNum = 1
	}

	lineNum := 0
	buffer := bufio.NewReader(file)
	for {
		var pageBuf string
		var err error
		if args.f {
			pageBuf, err = buffer.ReadString('\f')
			pageNum++
		} else {
			pageBuf, err = buffer.ReadString('\n')
			lineNum++
			if lineNum > args.l {
				pageNum++
				lineNum = 1
			}
		}

		if (err != nil) && (err != io.EOF) {
			fmt.Fprintf(os.Stderr, "Failed to read file!\n")
			os.Exit(3)
		}

		if (pageNum >= args.s) && (pageNum <= args.e) {
			if len(args.d) == 0 {
				res, ok := out.(*os.File)
				if ok {
					fmt.Fprintf(res, "%s", pageBuf)
				} else {
					fmt.Fprintf(os.Stderr, "Failed to print text!\n")
					os.Exit(4)
				}
			} else {
				res, ok := out.(io.WriteCloser)
				if ok {
					res.Write([]byte(pageBuf))
				} else {
					fmt.Fprintf(os.Stderr, "Failed to print text with pipe\n")
					os.Exit(4)
				}
			}
		}

		if err == io.EOF {
			break
		}
	}

	if pageNum < args.s {
		fmt.Fprintf(os.Stderr, "startPage(%d) bigger than total pages(%d), no output written!\n", args.s, pageNum)
		os.Exit(5)
	} else if pageNum < args.e {
		fmt.Fprintf(os.Stderr, "endPage(%d) bigger than total pages(%d), error!\n", args.e, pageNum)
		os.Exit(5)
	}

}

func main() {
	var a argsList
	var n []string
	initFlags(&a, &n)
	testFlags(&a, n)
	proecssArgs(&a)
}
